#pragma once

#include <string>
#include <vector>
#include <fstream>
#include <unordered_map>

#include <unistd.h>
#include <signal.h>
#include <pthread.h>

#include "socket.hpp"
#include "http.hpp"
#include "log.hpp"

extern Log log;

static const uint16_t default_port = 8081;
static const std::string default_ip = "0.0.0.0";

static std::string read_file(const std::string& path)
{
    std::ifstream fin(path, std::ios::in);
    if (!fin.is_open())
        return "";
    
    fin.seekg(0, std::ios_base::end);
    auto len = fin.tellg();
    fin.seekg(0, std::ios_base::beg);

    std::string content;
    content.resize(len);
    fin.read((char*)content.c_str(), content.size());
    // 读取二进制文件有问题
    // std::string line;
    // while (std::getline(fin, line))
    //     content += line;
    // fin.close();
    return content;
}

std::string suffix_to_desc(const std::string& suffix)
{
    static class ContentType
    {
    public:
        ContentType()
        {
            _types.insert({".html", "text/html"});
            _types.insert({".jpg", "image/jpeg"});
            _types.insert({".ico", "image/vnd.microsoft.icon"});
        }
        std::unordered_map<std::string, std::string> _types;
    } content_type;
    auto iter = content_type._types.find(suffix);
    if (iter == content_type._types.end())
        return content_type._types[".html"];
    else
        return iter->second;
}

struct thread_data
{
    int _sockfd;
};

static void* thread_run(void* args)
{
    thread_data* td = static_cast<thread_data*>(args);
    pthread_detach(pthread_self());
    char buffer[10240];
    // ssize_t n = read(td->_sockfd, buffer, sizeof(buffer) - 1);
    ssize_t n = recv(td->_sockfd, buffer, sizeof(buffer) - 1, 0);
    if (n > 0)
    {
        buffer[n] = '\0';
        std::cout << buffer;
        HttpRequest req;
        req.deserializer(buffer);
        req.parse();
        req.print();
        
        bool ok = true;
        // std::string text = read_file("./wwwroot/index.html");
        std::string text = read_file(req._file_path);
        if (text.empty())
        {
            ok = false;
            text = read_file("./wwwroot/err.html");
        }

        std::string response_line;
        if (ok)
            response_line = "HTTP/1.0 200 OK\r\n";
        else
            response_line = "HTTP/1.0 404 Not Found";
        std::string response_header = "Content-Length: ";
        response_header += std::to_string(text.size());
        response_header += "\r\n";
        response_header += "Content-Type: ";
        response_header += suffix_to_desc(req._suffix);
        response_header += "\r\n";
        // response_header += "Set-Cookie: name=haha";
        // response_header += "\r\n";
        // response_header += "Set-Cookie: passwd=123456";
        // response_header += "\r\n";
        std::string blank_line = "\r\n";

        std::string response = response_line;
        response += response_header;
        response += blank_line;
        response += text;
        send(td->_sockfd, response.c_str(), response.size(), 0);
    }
    close(td->_sockfd);
    delete (td);
    return nullptr;
}

class http_server
{
public:
    http_server(uint16_t port = default_port, const std::string& ip = default_ip)
        : _listen_socket(AF_INET)
        , _port(port)
        , _ip(ip)
    {}

    bool server_init()
    {
#ifdef __TEST__DEBUG__
        signal(SIGCHLD, SIG_IGN);
#else
        log.change_method(Class);
        daemon(1, 0);
#endif
        _listen_socket.Create(SOCK_STREAM);
        _listen_socket.Bind(_port, _ip);
        _listen_socket.Listen();
    }

    void start()
    {
        while (true)
        {
            std::string clientip;
            uint16_t clientport;
            int sockfd = _listen_socket.Accept(clientip, clientport);
            thread_data* td = new thread_data;
            td->_sockfd = sockfd;
            pthread_t tid;
            pthread_create(&tid, nullptr, thread_run, td);
        }
    }
private:
    Socket _listen_socket;
    uint16_t _port;
    std::string _ip;
};